package cur.nature.starter.common.config;

import cn.dev33.satoken.fun.SaParamFunction;
import cn.dev33.satoken.interceptor.SaInterceptor;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cur.nature.framework.core.log.LOG;
import cur.nature.framework.core.thread.MyThreadLocal;
import cur.nature.framework.core.util.JWTUtil;
import cur.nature.framework.core.util.StringUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {

    // 注册拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册 Sa-Token 拦截器，定义详细认证规则
        registry.addInterceptor(new SecurityInterceptor(handler -> {
            // 指定一条 match 规则
            SaRouter
                    .match("/**")    // 拦截的 path 列表，可以写多个 */
                    .notMatch("/admin/v1/api/Admin_User/login")
                    .notMatch("/admin/v1/api/Admin_User/logout")
                    .notMatch("/weixin/v1/Handle")
                    .notMatch("/v3/api-docs/**","/v3/api-docs/swagger-config","/doc.html", "/webjars/**")
                    .notMatch("/templates/**")
                    .check(r -> StpUtil.checkLogin());        // 要执行的校验动作，可以写完整的 lambda 表达式

            // 根据路由划分模块，不同模块不同鉴权
//            SaRouter.match("/component/satoken/v1/api/test/update", r -> StpUtil.checkPermission("user:update"));
            SaRouter.match("/component/satoken/v1/api/test/dashboard", r -> StpUtil.checkRole("admin"));
//            SaRouter.match("/admin/**", r -> StpUtil.checkPermission("admin"));
//            SaRouter.match("/goods/**", r -> StpUtil.checkPermission("goods"));
//            SaRouter.match("/orders/**", r -> StpUtil.checkPermission("orders"));
//            SaRouter.match("/notice/**", r -> StpUtil.checkPermission("notice"));
//            SaRouter.match("/comment/**", r -> StpUtil.checkPermission("comment"));
        })).addPathPatterns("/**");
    }




    public class SecurityInterceptor extends SaInterceptor {
        public SecurityInterceptor(SaParamFunction<Object> auth) {
            super.auth = auth;
        }
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            // 校验 satoken
            boolean satokenResult = false;
            try {
                satokenResult = super.preHandle(request,response,handler);
            }catch (Exception e) {
                LOG.warn(e.getMessage());
            }
            if (satokenResult) {return true;}
            if ("OPTIONS".equals(request.getMethod())){
                return true;
            }

            // 校验 jwt
            final String token = validateJwt(request, response, handler);
            if (token != null) {
                final Long userId = Long.valueOf(token);
                if (!StpUtil.isLogin(userId)) {
                    StpUtil.login(Long.valueOf(token));
                }
                return true;
            }
            return true; // 测试环境下，放开校验
//            StpUtil.checkLogin();

        }
    }

    public String validateJwt(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String token = request.getHeader(MyThreadLocal.TOKEN);
        if(StringUtil.isEmpty(token)){
            LOG.warn("token 为空，校验失败。");return null;}
        //校验token
        JWTUtil.Result result = JWTUtil.decodeToken(token);
        String data = result.getData();
        //校验通过
        if(JWTUtil.Result.OK == result.getStatus()){return data;}
        //超时
        if (JWTUtil.Result.EXPIRE == result.getStatus()) {
            //方案一：过期时长在一个小时之内，则自动刷新token, 一个小时之后的报错
            long expireHours = (result.getExpireSeconds() / 3600);
            if (expireHours <= 0) {
                String newToken = JWTUtil.encodeToken(data);
                response.addHeader(MyThreadLocal.TOKEN, newToken);
                return data;
            }
            return null;
        }
        return null;
    }

}

